home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / Tools / lynx-2.4 / WWW / Library / Implementation / HTGopher.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-06-28  |  22.7 KB  |  901 lines

  1. /*            GOPHER ACCESS                HTGopher.c
  2. **            =============
  3. **
  4. ** History:
  5. **    26 Sep 90    Adapted from other accesses (News, HTTP) TBL
  6. **    29 Nov 91    Downgraded to C, for portable implementation.
  7. */
  8.  
  9. #include "HTUtils.h"        /* Coding convention macros */
  10. #include "tcp.h"
  11. #include "HTAlert.h"
  12. #include "HTParse.h"
  13.  
  14. /* Implements:
  15. */
  16. #include "HTGopher.h"
  17.  
  18. #define HT_EM_SPACE ((char)2)           /* For now */
  19.  
  20. #define GOPHER_PORT 70        /* See protocol spec */
  21. #define BIG 1024        /* Bug */
  22. #define LINE_LENGTH 256        /* Bug */
  23.  
  24. /*    Gopher entity types:
  25. */
  26. #define GOPHER_TEXT        '0'
  27. #define GOPHER_MENU        '1'
  28. #define GOPHER_CSO        '2'
  29. #define GOPHER_ERROR        '3'
  30. #define GOPHER_MACBINHEX    '4'
  31. #define GOPHER_PCBINARY        '5'
  32. #define GOPHER_UUENCODED    '6'
  33. #define GOPHER_INDEX        '7'
  34. #define GOPHER_TELNET        '8'
  35. #define GOPHER_BINARY           '9'
  36. #define GOPHER_GIF              'g'
  37. #define GOPHER_HTML        'h'            /* HTML */
  38. #define GOPHER_CHTML        'H'            /* HTML */
  39. #define GOPHER_SOUND            's'
  40. #define GOPHER_WWW        'w'        /* W3 address */
  41. #define GOPHER_IMAGE            'I'
  42. #define GOPHER_TN3270           'T'
  43. #define GOPHER_INFO             'i'
  44. #define GOPHER_DUPLICATE    '+'
  45. #define GOPHER_PLUS_IMAGE    ':'        /* Addition from Gopher Plus */
  46. #define GOPHER_PLUS_MOVIE    ';'
  47. #define GOPHER_PLUS_SOUND    '<'
  48. #define GOPHER_PLUS_PDF        'P'
  49.  
  50. #include <ctype.h>
  51.  
  52. #include "HTParse.h"
  53. #include "HTFormat.h"
  54. #include "HTTCP.h"
  55.  
  56. /*        Hypertext object building machinery
  57. */
  58. #include "HTML.h"
  59.  
  60. #include "LYLeaks.h"
  61.  
  62. #define PUTC(c) (*targetClass.put_character)(target, c)
  63. #define PUTS(s) (*targetClass.put_string)(target, s)
  64. #define START(e) (*targetClass.start_element)(target, e, 0, 0)
  65. #define END(e) (*targetClass.end_element)(target, e)
  66. #define FREE_TARGET (*targetClass._free)(target)
  67. struct _HTStructured {
  68.     CONST HTStructuredClass *    isa;
  69.     /* ... */
  70. };
  71.  
  72. PRIVATE HTStructured *target;            /* the new hypertext */
  73. PRIVATE HTStructuredClass targetClass;        /* Its action routines */
  74.  
  75.  
  76. #define GOPHER_PROGRESS(foo) HTAlert(foo)
  77.  
  78.  
  79. #define NEXT_CHAR HTGetCharacter() 
  80.  
  81.  
  82.  
  83. /*    Module-wide variables
  84. */
  85. PRIVATE int s;                    /* Socket for GopherHost */
  86.  
  87.  
  88.  
  89. /*    Matrix of allowed characters in filenames
  90. **    -----------------------------------------
  91. */
  92.  
  93. PRIVATE BOOL acceptable[256];
  94. PRIVATE BOOL acceptable_inited = NO;
  95.  
  96. PRIVATE void init_acceptable NOARGS
  97. {
  98.     unsigned int i;
  99.     char * good = 
  100.       "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./-_$";
  101.     for(i=0; i<256; i++) acceptable[i] = NO;
  102.     for(;*good; good++) acceptable[(unsigned int)*good] = YES;
  103.     acceptable_inited = YES;
  104. }
  105.  
  106. PRIVATE CONST char hex[17] = "0123456789abcdef";
  107.  
  108. /*    Decdoe one hex character
  109. */
  110.  
  111. PRIVATE char from_hex ARGS1(char, c)
  112. {
  113.     return           (c>='0')&&(c<='9') ? c-'0'
  114.             : (c>='A')&&(c<='F') ? c-'A'+10
  115.             : (c>='a')&&(c<='f') ? c-'a'+10
  116.             :               0;
  117. }
  118.  
  119.  
  120.  
  121. /*    Paste in an Anchor
  122. **    ------------------
  123. **
  124. **    The title of the destination is set, as there is no way
  125. **    of knowing what the title is when we arrive.
  126. **
  127. ** On entry,
  128. **    HT     is in append mode.
  129. **    text     points to the text to be put into the file, 0 terminated.
  130. **    addr    points to the hypertext refernce address 0 terminated.
  131. */
  132. PUBLIC BOOLEAN HT_Is_Gopher_URL=FALSE;
  133.  
  134. PRIVATE void write_anchor ARGS2(CONST char *,text, CONST char *,addr)
  135. {
  136.  
  137.  
  138.     
  139.     BOOL present[HTML_A_ATTRIBUTES];
  140.     CONST char * value[HTML_A_ATTRIBUTES];
  141.     
  142.     int i;
  143.     
  144.     for (i=0; i<HTML_A_ATTRIBUTES; i++) present[i]=0;
  145.     present[HTML_A_HREF] = YES;
  146.     ((CONST char **)value)[HTML_A_HREF] = addr;
  147.     present[HTML_A_TITLE] = YES;
  148.     ((CONST char **)value)[HTML_A_TITLE] = text;
  149.  
  150.     if(TRACE)
  151.     fprintf(stderr,"HTGopher: adding URL: %s\n",addr);
  152.     
  153.     HT_Is_Gopher_URL = TRUE;  /* tell HTML.c that this is a Gopher URL */
  154.     (*targetClass.start_element)(target, HTML_A, present,(CONST char **)value);
  155.         
  156.     PUTS(text);
  157.     END(HTML_A);
  158. }
  159.  
  160.  
  161. /*    Parse a Gopher Menu document
  162. **    ============================
  163. **
  164. */
  165.  
  166. PRIVATE void parse_menu ARGS2 (
  167.     CONST char *,        arg,
  168.     HTParentAnchor *,    anAnchor)
  169. {
  170.     char gtype;
  171.     char ch;
  172.     char line[BIG];
  173.     char address[BIG];
  174.     char *name, *selector;        /* Gopher menu fields */
  175.     char *host;
  176.     char *port;
  177.     char *p = line;
  178.     CONST char *title;
  179.     int bytes=0;
  180.     int BytesReported = 0;
  181.     char buffer[128];
  182.     extern int interrupted_in_htgetcharacter;
  183.  
  184. #define TAB         '\t'
  185. #define HEX_ESCAPE     '%'
  186.  
  187.     
  188.     START(HTML_HTML);
  189.     PUTS("\n");
  190.     START(HTML_HEAD);
  191.     PUTS("\n");
  192.     START(HTML_TITLE);
  193.     if ((title = HTAnchor_title(anAnchor)))
  194.     PUTS(title);
  195.     else
  196.         PUTS("Gopher Menu");
  197.     END(HTML_TITLE);
  198.     PUTS("\n");
  199.     END(HTML_HEAD);
  200.     PUTS("\n");
  201.  
  202.     START(HTML_BODY);
  203.     PUTS("\n");
  204.     START(HTML_H1);
  205.     if ((title = HTAnchor_title(anAnchor)))
  206.     PUTS(title);
  207.     else
  208.         PUTS("Gopher Menu");
  209.     END(HTML_H1);
  210.     PUTS("\n");
  211.     START(HTML_PRE);
  212.     while ((ch=NEXT_CHAR) != (char)EOF) {
  213.  
  214.     if (interrupted_in_htgetcharacter) {
  215.         if (TRACE) fprintf (stderr,
  216.         "Gopher: Interrupted in HTGetCharacter, apparently.\n");
  217.         goto end_html;
  218.         }
  219.  
  220.         if (ch != LF) {
  221.         *p = ch;        /* Put character in line */
  222.         if (p< &line[BIG-1]) p++;
  223.         
  224.     } else {
  225.         *p++ = 0;        /* Terminate line */
  226.         bytes += p-line;    /* add size */
  227.         p = line;        /* Scan it to parse it */
  228.         port = 0;        /* Flag "not parsed" */
  229.         if (TRACE) fprintf(stderr, "HTGopher: Menu item: %s\n", line);
  230.         gtype = *p++;
  231.  
  232.         if (bytes > BytesReported + 1024) {
  233.             sprintf(buffer, "Transferred %d bytes", bytes);
  234.                 HTProgress(buffer);
  235.         BytesReported = bytes;
  236.         }
  237.         
  238.         /* Break on line with a dot by itself */
  239.         if ((gtype=='.') && ((*p=='\r') || (*p==0))) break;
  240.  
  241.         if (gtype && *p) {
  242.             name = p;
  243.         selector = strchr(name, TAB);
  244.         if (selector) {
  245.             *selector++ = 0;    /* Terminate name */
  246.             /*
  247.              * Gopher+ Type=0+ objects can be binary, and will
  248.              * have 9 or 5 beginning their selector.  Make sure
  249.              * we don't trash the terminal by treating them as
  250.              * text. - FM
  251.              */
  252.             if (gtype == GOPHER_TEXT && (*selector == GOPHER_BINARY ||
  253.                              *selector == GOPHER_PCBINARY))
  254.                 gtype = *selector;
  255.             host = strchr(selector, TAB);
  256.             if (host) {
  257.             *host++ = 0;    /* Terminate selector */
  258.             port = strchr(host, TAB);
  259.             if (port) {
  260.                 char *junk;
  261.                 port[0] = ':';    /* delimit host a la W3 */
  262.                 junk = strchr(port, TAB);
  263.                 if (junk) *junk++ = 0;    /* Chop port */
  264.                 if ((port[1]=='0') && (!port[2]))
  265.                     port[0] = 0;    /* 0 means none */
  266.             } /* no port */
  267.             } /* host ok */
  268.         } /* selector ok */
  269.         } /* gtype and name ok */
  270.         
  271.         /* Nameless files are a separator line */
  272.         if (gtype == GOPHER_TEXT) {
  273.             int i = strlen(name)-1;
  274.         while (name[i] == ' ' && i >= 0)
  275.             name[i--] = '\0';
  276.         if (i < 0)
  277.             gtype = GOPHER_INFO;
  278.         }
  279.  
  280.         if (gtype == GOPHER_WWW) {    /* Gopher pointer to W3 */
  281.         PUTS("(HTML) ");
  282.         write_anchor(name, selector);
  283.  
  284.         } else if (gtype == GOPHER_INFO) {
  285.         /* Information or separator line */
  286.         PUTS("       ");
  287.         PUTS(name);
  288.  
  289.         } else if (port) {        /* Other types need port */
  290.         if (gtype == GOPHER_TELNET) {
  291.             PUTS(" (TEL) ");
  292.             if (*selector) sprintf(address, "telnet://%s@%s/",
  293.                        selector, host);
  294.             else sprintf(address, "telnet://%s/", host);
  295.         }
  296.         else if (gtype == GOPHER_TN3270) 
  297.         {
  298.             PUTS("(3270) ");
  299.             if (*selector) 
  300.             sprintf(address, "tn3270://%s@%s/",
  301.                 selector, host);
  302.             else 
  303.             sprintf(address, "tn3270://%s/", host);
  304.         }
  305.         else {            /* If parsed ok */
  306.             char *q;
  307.             char *p;
  308.  
  309.             switch(gtype) {
  310.                        case GOPHER_TEXT:
  311.                            PUTS("(FILE) ");
  312.                            break;
  313.                     case GOPHER_MENU:
  314.                             PUTS(" (DIR) ");
  315.                             break;
  316.                     case GOPHER_CSO:
  317.                             PUTS(" (CSO) ");
  318.                             break;
  319.                     case GOPHER_PCBINARY:
  320.                             PUTS(" (BIN) ");
  321.                             break;
  322.                     case GOPHER_UUENCODED:
  323.                             PUTS(" (UUE) ");
  324.                             break;
  325.                     case GOPHER_INDEX:
  326.                             PUTS("  (?)  ");
  327.                             break;
  328.                     case GOPHER_BINARY:
  329.                             PUTS(" (BIN) ");
  330.                             break;
  331.                     case GOPHER_GIF:
  332.                     case GOPHER_IMAGE:
  333.                     case GOPHER_PLUS_IMAGE:
  334.                             PUTS(" (IMG) ");
  335.                             break;
  336.                     case GOPHER_SOUND:
  337.                     case GOPHER_PLUS_SOUND:
  338.                             PUTS(" (SND) ");
  339.                             break;
  340.                     case GOPHER_MACBINHEX:
  341.                             PUTS(" (HQX) ");
  342.                             break;
  343.             case GOPHER_HTML:
  344.             case GOPHER_CHTML:
  345.                             PUTS("(HTML) ");
  346.                             break;
  347.                     case 'm':
  348.                             PUTS("(MIME) ");
  349.                             break;
  350.                     case GOPHER_PLUS_MOVIE:
  351.                             PUTS(" (MOV) ");
  352.                             break;
  353.                     case GOPHER_PLUS_PDF:
  354.                             PUTS(" (PDF) ");
  355.                             break;
  356.                     default:
  357.                             PUTS("(UNKN) ");
  358.                             break;
  359.             }
  360.  
  361.             sprintf(address, "//%s/%c", host, gtype);
  362.  
  363.             q = address+ strlen(address);
  364.             for(p=selector; *p; p++) {    /* Encode selector string */
  365.             if (acceptable[*p]) *q++ = *p;
  366.             else {
  367.                 *q++ = HEX_ESCAPE;    /* Means hex coming */
  368.                 *q++ = hex[(TOASCII(*p)) >> 4];
  369.                 *q++ = hex[(TOASCII(*p)) & 15];
  370.             }
  371.             }
  372.  
  373.             *q++ = 0;            /* terminate address */
  374.         }
  375.         /* Error response from Gopher doesn't deserve to
  376.            be a hyperlink. */
  377.         if (strcmp (address, "gopher://error.host:1/0"))
  378.             write_anchor(name, address);
  379.         else
  380.             PUTS(name);
  381.         } else { /* parse error */
  382.             if (TRACE) fprintf(stderr,
  383.             "HTGopher: Bad menu item.\n");
  384.         PUTS(line);
  385.  
  386.         } /* parse error */
  387.         
  388.         PUTS("\n");
  389.         p = line;    /* Start again at beginning of line */
  390.         
  391.     } /* if end of line */
  392.     
  393.     } /* Loop over characters */
  394.     
  395. end_html:
  396.     END(HTML_PRE);
  397.     PUTS("\n");
  398.     END(HTML_BODY);
  399.     PUTS("\n");
  400.     END(HTML_HTML);
  401.     PUTS("\n");
  402.     FREE_TARGET;
  403.     
  404.     return;
  405. }
  406. /*    Parse a Gopher CSO document
  407.  **    ============================
  408.  **
  409.  **   Accepts an open socket to a CSO server waiting to send us
  410.  **   data and puts it on the screen in a reasonable manner.
  411.  **
  412.  **   Perhaps this data can be automatically linked to some
  413.  **   other source as well???
  414.  **
  415.  **  Taken from hacking by Lou Montulli@ukanaix.cc.ukans.edu
  416.  **  on XMosaic-1.1, and put on libwww 2.11 by Arthur Secret, 
  417.  **  secret@dxcern.cern.ch .
  418.  */
  419.  
  420. PRIVATE void parse_cso ARGS2 (
  421.                   CONST char *,    arg,
  422.                   HTParentAnchor *,anAnchor)
  423. {
  424.     char ch;
  425.     char line[BIG];
  426.     char *p = line;
  427.     char *second_colon, last_char='\0';
  428.     CONST char *title;
  429.     
  430.     START(HTML_HEAD);
  431.     PUTS("\n");
  432.     START(HTML_TITLE);
  433.     if ((title = HTAnchor_title(anAnchor)))
  434.         PUTS(title);
  435.     else
  436.         PUTS("CSO Search Results");
  437.     END(HTML_TITLE);
  438.     PUTS("\n");
  439.     END(HTML_HEAD);
  440.     PUTS("\n");
  441.     START(HTML_H1);
  442.     if ((title = HTAnchor_title(anAnchor)))
  443.         PUTS(title);
  444.     else {
  445.         PUTS(arg);
  446.         PUTS(" Search Results");
  447.     }
  448.     END(HTML_H1);
  449.     PUTS("\n");
  450.     START(HTML_PRE);
  451.  
  452.     /* start grabbing chars from the network */
  453.     while ((ch=NEXT_CHAR) != (char)EOF) 
  454.     {
  455.         if (ch != '\n') 
  456.         {
  457.             *p = ch;        /* Put character in line */
  458.             if (p< &line[BIG-1]) p++;
  459.         } 
  460.         else 
  461.         {
  462.             *p++ = 0;        /* Terminate line */
  463.             p = line;        /* Scan it to parse it */
  464.             
  465.             /* OK we now have a line in 'p' lets parse it and 
  466.                print it */
  467.             
  468.             /* Break on line that begins with a 2. It's the end of
  469.              * data.
  470.              */
  471.             if (*p == '2')
  472.             break;
  473.             
  474.             /*  lines beginning with 5 are errors, 
  475.              *  print them and quit
  476.              */
  477.             if (*p == '5') {
  478.             START(HTML_H2);
  479.             PUTS(p+4);
  480.             END(HTML_H2);
  481.             break;
  482.             }
  483.             
  484.             if(*p == '-') {
  485.             /*  data lines look like  -200:#:
  486.              *  where # is the search result number and can be  
  487.              *  multiple digits (infinate?)
  488.              *  find the second colon and check the digit to the
  489.              *  left of it to see if they are diferent
  490.              *  if they are then a different person is starting. 
  491.              *  make this line an <h2>
  492.              */
  493.             
  494.             /* find the second_colon */
  495.             second_colon = strchr( strchr(p,':')+1, ':');
  496.             
  497.             if(second_colon != NULL) {  /* error check */
  498.                 
  499.                 if (*(second_colon-1) != last_char)   
  500.                 /* print seperator */
  501.                 {
  502.                 END(HTML_PRE);
  503.                 START(HTML_H2);
  504.                 }
  505.                 
  506.                 
  507.                 /* right now the record appears with the alias 
  508.                  * (first line)
  509.                  * as the header and the rest as <pre> text
  510.                  * It might look better with the name as the
  511.                  * header and the rest as a <ul> with <li> tags
  512.                  * I'm not sure whether the name field comes in any
  513.                  * special order or if its even required in a 
  514.                  * record,
  515.                  * so for now the first line is the header no 
  516.                  * matter
  517.                  * what it is (it's almost always the alias)
  518.                  * A <dl> with the first line as the <DT> and
  519.                  * the rest as some form of <DD> might good also?
  520.                  */
  521.                 
  522.                 /* print data */
  523.                 PUTS(second_colon+1);
  524.                 PUTS("\n");
  525.                 
  526.                 if (*(second_colon-1) != last_char)   
  527.                 /* end seperator */
  528.                 {
  529.                 END(HTML_H2);
  530.                 START(HTML_PRE);
  531.                 }
  532.                                 
  533.                 /* save the char before the second colon
  534.                  * for comparison on the next pass
  535.                  */
  536.                 last_char =  *(second_colon-1) ;
  537.                 
  538.             } /* end if second_colon */
  539.             } /* end if *p == '-' */
  540.         } /* if end of line */
  541.         
  542.     } /* Loop over characters */
  543.     
  544.     /* end the text block */
  545.     PUTS("\n");
  546.     END(HTML_PRE);
  547.     PUTS("\n");
  548.     FREE_TARGET;
  549.  
  550.     return;  /* all done */
  551. } /* end of procedure */
  552.  
  553. /*    Display a Gopher Index document
  554.  **    -------------------------------
  555.  */
  556.  
  557. PRIVATE void display_index ARGS2 (
  558.                   CONST char *,    arg,
  559.                   HTParentAnchor *,anAnchor)
  560. {
  561.     CONST char * title;
  562.     
  563.     START(HTML_HEAD);
  564.     PUTS("\n");
  565.     PUTS("\n");
  566.     START(HTML_TITLE);
  567.     if ((title = HTAnchor_title(anAnchor)))
  568.     PUTS(title);
  569.     else
  570.         PUTS("Gopher index");
  571.     END(HTML_TITLE);
  572.     PUTS("\n");
  573.     START(HTML_ISINDEX);
  574.     PUTS("\n");
  575.     END(HTML_HEAD);
  576.     PUTS("\n");
  577.     START(HTML_H1);
  578.     if ((title = HTAnchor_title(anAnchor)))
  579.     PUTS(title);
  580.     else {
  581.        PUTS(arg);
  582.        PUTS(" index");
  583.     }
  584.     END(HTML_H1);
  585.     PUTS("\nThis is a searchable Gopher index.\n");
  586.     START(HTML_P);
  587.     PUTS("\nPlease enter search keywords.\n");
  588.     
  589.     if (!HTAnchor_title(anAnchor))
  590.         HTAnchor_setTitle(anAnchor, arg);
  591.     
  592.     FREE_TARGET;
  593.     return;
  594. }
  595.  
  596.  
  597. /*      Display a CSO index document
  598. **      -------------------------------
  599. */
  600.  
  601. PRIVATE void display_cso ARGS2 (
  602.         CONST char *,   arg,
  603.         HTParentAnchor *,anAnchor)
  604. {
  605.     CONST char * title;
  606.  
  607.     START(HTML_HEAD);
  608.     PUTS("\n");
  609.     START(HTML_TITLE);
  610.     if ((title = HTAnchor_title(anAnchor)))
  611.     PUTS(title);
  612.     else
  613.         PUTS("CSO index");
  614.     END(HTML_TITLE);
  615.     PUTS("\n");
  616.     START(HTML_ISINDEX);
  617.     PUTS("\n");
  618.     END(HTML_HEAD);
  619.     PUTS("\n");
  620.     START(HTML_H1);
  621.     if ((title = HTAnchor_title(anAnchor)))
  622.     PUTS(title);
  623.     else {
  624.        PUTS(arg);
  625.        PUTS(" index");
  626.     }
  627.     END(HTML_H1);
  628.     PUTS("\nThis is a searchable index of a CSO database.\n");
  629.     START(HTML_P);
  630.     PUTS("\nPress the 's' key and enter search keywords.\n"); 
  631.     START(HTML_P);
  632.     PUTS("\nThe keywords that you enter will allow you to search on a");
  633.     PUTS(" person's name in the database.\n");
  634.  
  635.     if (!HTAnchor_title(anAnchor))
  636.         HTAnchor_setTitle(anAnchor, arg);
  637.     
  638.     FREE_TARGET;
  639.     return;
  640. }
  641.  
  642.  
  643. /*        De-escape a selector into a command
  644. **        -----------------------------------
  645. **
  646. **    The % hex escapes are converted. Otheriwse, the string is copied.
  647. */
  648. PRIVATE void de_escape ARGS2(char *, command, CONST char *, selector)
  649. {
  650.     CONST char * p = selector;
  651.     char * q = command;
  652.     if (command == NULL) outofmem(__FILE__, "HTLoadGopher");
  653.     while (*p) {        /* Decode hex */
  654.     if (*p == HEX_ESCAPE) {
  655.         char c;
  656.         unsigned int b;
  657.         p++;
  658.         c = *p++;
  659.         b =   from_hex(c);
  660.         c = *p++;
  661.         if (!c) break;    /* Odd number of chars! */
  662.         *q++ = FROMASCII((b<<4) + from_hex(c));
  663.     } else {
  664.         *q++ = *p++;    /* Record */
  665.     }
  666.     }
  667.     *q++ = 0;    /* Terminate command */
  668.  
  669. }
  670.  
  671.  
  672. /*        Load by name                    HTLoadGopher
  673. **        ============
  674. **
  675. **     Bug:    No decoding of strange data types as yet.
  676. **
  677. */
  678. PUBLIC int HTLoadGopher ARGS4(
  679.     CONST char *,        arg,
  680.     HTParentAnchor *,    anAnchor,
  681.     HTFormat,        format_out,
  682.     HTStream*,        sink)
  683. {
  684.     char *command;            /* The whole command */
  685.     int status;                /* tcp return */
  686.     char gtype;                /* Gopher Node type */
  687.     char * selector;            /* Selector string */
  688.  
  689.     struct sockaddr_in soc_address;    /* Binary network address */
  690.     struct sockaddr_in* sin = &soc_address;
  691.     
  692.     if (!acceptable_inited) init_acceptable();
  693.     
  694.     if (!arg) return -3;        /* Bad if no name sepcified    */
  695.     if (!*arg) return -2;        /* Bad if name had zero length    */
  696.     
  697.     if (TRACE) fprintf(stderr, "HTGopher: Looking for %s\n", arg);
  698.     
  699.     
  700. /*  Set up defaults:
  701. */
  702.     sin->sin_family = AF_INET;                /* Family, host order  */
  703.     sin->sin_port = htons(GOPHER_PORT);            /* Default: new port,  */
  704.  
  705. /* Get node name and optional port number:
  706. */
  707.     {
  708.     char *p1 = HTParse(arg, "", PARSE_HOST);
  709.     int status = HTParseInet(sin, p1);
  710.         free(p1);
  711.         if (status) return status;   /* Bad */
  712.     }
  713.     
  714. /* Get entity type, and selector string.
  715. */        
  716.     {
  717.     char * p1 = HTParse(arg, "", PARSE_PATH|PARSE_PUNCTUATION);
  718.         gtype = '1';        /* Default = menu */
  719.     selector = p1;
  720.     if ((*selector++=='/') && (*selector)) {    /* Skip first slash */
  721.         gtype = *selector++;            /* Pick up gtype */
  722.     }
  723.     if (gtype == GOPHER_INDEX) {
  724.         char * query;
  725.         /* Search is allowed */
  726.             HTAnchor_setIndex(anAnchor, anAnchor->address);    
  727.         query = strchr(selector, '?');    /* Look for search string */
  728.         if (!query || !query[1]) {        /* No search required */
  729.         target = HTML_new(anAnchor, format_out, sink);
  730.         targetClass = *target->isa;
  731.         display_index(arg, anAnchor);    /* Display "cover page" */
  732.         return HT_LOADED;        /* Local function only */
  733.         }
  734.         *query++ = 0;            /* Skip '?'     */
  735.         command = malloc(strlen(selector)+ 1 + strlen(query)+ 2 + 1);
  736.               if (command == NULL) outofmem(__FILE__, "HTLoadGopher");
  737.           
  738.         de_escape(command, selector);    /* Bug fix TBL 921208 */
  739.  
  740.         strcat(command, "\t");
  741.       
  742.         {                    /* Remove plus signs 921006 */
  743.             char *p;
  744.         for (p=query; *p; p++) {
  745.             if (*p == '+') *p = ' ';
  746.         }
  747.         }
  748.  
  749.         de_escape(&command[strlen(command)], query);/* bug fix LJM 940415 */
  750.         } else if (gtype == GOPHER_CSO) {
  751.             char * query;
  752.         /* Search is allowed */
  753.             HTAnchor_setIndex(anAnchor, anAnchor->address);    
  754.             query = strchr(selector, '?');      /* Look for search string */
  755.             if (!query || !query[1]) {          /* No search required */
  756.         target = HTML_new(anAnchor, format_out, sink);
  757.         targetClass = *target->isa;
  758.                 display_cso(arg, anAnchor);     /* Display "cover page" */
  759.                 return HT_LOADED;                 /* Local function only */
  760.             }
  761.             *query++ = 0;                       /* Skip '?'     */
  762.             command = malloc(strlen("query")+ 1 + strlen(query)+ 2 + 1);
  763.               if (command == NULL) outofmem(__FILE__, "HTLoadGopher");
  764.  
  765.             de_escape(command, selector);       /* Bug fix TBL 921208 */
  766.  
  767.             strcpy(command, "query ");
  768.  
  769.             {                                   /* Remove plus signs 921006 */
  770.                 char *p;
  771.                 for (p=query; *p; p++) {
  772.                     if (*p == '+') *p = ' ';
  773.                 }
  774.             }
  775.         de_escape(&command[strlen(command)], query);/* bug fix LJM 940415 */
  776.  
  777.         
  778.     } else {                /* Not index */
  779.         command = malloc(strlen(selector)+2+1);
  780.         de_escape(command, selector);
  781.     }
  782.     free(p1);
  783.     }
  784.     
  785.     {
  786.     char * p = command + strlen(command);
  787.     *p++ = CR;        /* Macros to be correct on Mac */
  788.     *p++ = LF;
  789.     *p++ = 0;
  790.     /* strcat(command, "\r\n");    */    /* CR LF, as in rfc 977 */
  791.     }
  792.  
  793. /*    Set up a socket to the server for the data:
  794. */      
  795.  
  796.   status = HTDoConnect (arg, "Gopher", 70, &s);
  797.   if (status == HT_INTERRUPTED)
  798.     {
  799.       /* Interrupt cleanly. */
  800.       if (TRACE)
  801.         fprintf (stderr,
  802.                  "Gopher: Interrupted on connect; recovering cleanly.\n");
  803.       _HTProgress ("Connection interrupted.");
  804.       return HT_INTERRUPTED;
  805.     }
  806.   if (status<0){
  807.     if (TRACE) fprintf(stderr, "HTTPAccess: Unable to connect to remote host for `%s'.\n",
  808.         arg);
  809.     free(command);
  810.     return HTInetStatus("connect");
  811.   }
  812.     
  813.     HTInitInput(s);        /* Set up input buffering */
  814.     
  815.     if (TRACE) fprintf(stderr, "HTGopher: Connected, writing command `%s' to socket %d\n", command, s);
  816.     
  817. #ifdef NOT_ASCII
  818.     {
  819.         char * p;
  820.     for(p = command; *p; p++) {
  821.         *p = TOASCII(*p);
  822.     }
  823.     }
  824. #endif
  825.  
  826.     _HTProgress ("Sending Gopher request.");
  827.  
  828.     status = NETWRITE(s, command, (int)strlen(command));
  829.     free(command);
  830.     if (status<0){
  831.     if (TRACE) fprintf(stderr, "HTGopher: Unable to send command.\n");
  832.         return HTInetStatus("send");
  833.     }
  834.  
  835.     _HTProgress ("Gopher request sent; waiting for response.");
  836.  
  837. /*    Now read the data from the socket:
  838. */    
  839.     switch (gtype) {
  840.     
  841.     case GOPHER_TEXT :
  842.          HTParseSocket(WWW_PLAINTEXT, format_out, anAnchor, s, sink);
  843.     break;
  844.  
  845.     case GOPHER_HTML :
  846.     case GOPHER_CHTML :
  847.         HTParseSocket(WWW_HTML, format_out, anAnchor, s, sink);
  848.     break;
  849.  
  850.     case GOPHER_GIF:
  851.     case GOPHER_IMAGE:
  852.     case GOPHER_PLUS_IMAGE:
  853.         HTParseSocket(HTAtom_for("image/gif"), 
  854.                format_out, anAnchor, s, sink);
  855.       break;
  856.  
  857.     case GOPHER_MENU :
  858.     case GOPHER_INDEX :
  859.     target = HTML_new(anAnchor, format_out, sink);
  860.     targetClass = *target->isa;
  861.         parse_menu(arg, anAnchor);
  862.     break;
  863.      
  864.     case GOPHER_CSO:
  865.     target = HTML_new(anAnchor, format_out, sink);
  866.     targetClass = *target->isa;
  867.           parse_cso(arg, anAnchor);
  868.     break;
  869.        
  870.     case GOPHER_SOUND :
  871.     case GOPHER_PLUS_SOUND :
  872.         HTParseSocket(WWW_AUDIO, format_out, anAnchor, s, sink);
  873.     break;
  874.     
  875.     case GOPHER_PLUS_MOVIE:
  876.         HTParseSocket(HTAtom_for("video/mpeg"), format_out, anAnchor, s, sink);
  877.     break;
  878.  
  879.     case GOPHER_PLUS_PDF:
  880.         HTParseSocket(HTAtom_for("application/pdf"), format_out, anAnchor,
  881.                   s, sink);
  882.     break;
  883.  
  884.     case GOPHER_MACBINHEX:
  885.     case GOPHER_PCBINARY:
  886.     case GOPHER_UUENCODED:
  887.     case GOPHER_BINARY:
  888.     default:
  889.         /* Specifying WWW_UNKNOWN forces dump to local disk. */
  890.         HTParseSocket (WWW_UNKNOWN, format_out, anAnchor, s, sink);
  891.     break;
  892.  
  893.     } /* switch(gtype) */
  894.  
  895.     NETCLOSE(s);
  896.     return HT_LOADED;
  897. }
  898.  
  899. GLOBALDEF PUBLIC HTProtocol HTGopher = { "gopher", HTLoadGopher, NULL };
  900.  
  901.